﻿using AutoMapper;
using DotNetCore.CAP;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using X23.Application.Dto;
using X23.Application.ExcelManagement.Dto;
using X23.EFCore.Repository;
using X23.Model.Excel;
using X23.Util.ConvertTypeUtil;
using X23.Util.LinqUtil;
using X23.Util.SerializationUtil;
using static X23.Model.Const.Enum;

namespace X23.Application.ExcelManagement
{
    public class ExcelManagementService : IExcelManagementService, ICapSubscribe
    {
        private readonly IServiceProvider _serviceProvider;
        private readonly IWebHostEnvironment _webHostEnvironment;

        /// <summary>
        /// 文件夹路径
        /// </summary>
        private readonly string _folderPath;
        private readonly ICapPublisher _capBus;
        private readonly IMapper _mapper;
        private readonly IRepository<TaskPerformance> _taskPerformanceRepository;

        public ExcelManagementService(
            IServiceProvider serviceProvider,
            ICapPublisher capPublisher,
            IMapper mapper,
            IRepository<TaskPerformance> taskPerformanceRepository,
            IWebHostEnvironment webHostEnvironment
            )
        {
            _serviceProvider = serviceProvider;
            _webHostEnvironment = webHostEnvironment;
            _folderPath = Path.Combine(webHostEnvironment.ContentRootPath, "File", "Excel");
            _capBus = capPublisher;
            _mapper = mapper;
            _taskPerformanceRepository = taskPerformanceRepository;
        }

        public MemoryStream DownFileStream(string guid, string fileType)
        {
            string excelPath = Path.Combine(_folderPath, $"{guid}.{fileType}");
            var fileStream = new FileStream(excelPath, FileMode.Open, FileAccess.Read, FileShare.Read); 
            byte[] bytes = new byte[fileStream.Length];
            fileStream.Read(bytes, 0, bytes.Length);
            fileStream.Close();

           return new MemoryStream(bytes);
        }

        #region 有问题
        //public async Task<string> Export(string code, string fileName, object obj, string method, object parameters, bool isCap = false)
        //{
        //    var guid = Guid.NewGuid().ToString("N");//32GUID
        //    var taskPerformance = new TaskPerformance
        //    {
        //        TaskExeStatus = TaskExeStatus.No,
        //        Code = code,
        //        FileName = $"{fileName}",
        //        Guid = guid
        //    };
        //    await _taskPerformanceRepository.AddAsync(taskPerformance);
        //    var dto = new ExportCapDto { Method = method, ControllerTypeName = obj.ToString(), Parameters = parameters, TaskPerformance = taskPerformance };

        //    if (isCap)
        //    {
        //        _capBus.Publish("ExportCap", dto);
        //    }
        //    else
        //    {
        //        await ExportCap(dto);
        //    }

        //    return guid;
        //}

        //[CapSubscribe("ExportCap")]
        //public async Task ExportCap(ExportCapDto exportCapDto)
        //{
        //    var taskPerformance = exportCapDto.TaskPerformance;

        //    Assembly asm = Assembly.Load("X23");
        //    Type t = asm.GetType(exportCapDto.ControllerTypeName);
        //    var obj = ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, t);
        //    var method = obj.GetType().GetMethod(exportCapDto.Method);
        //    var parameters = method.GetParameters()
        //        .Select(x => JsonConvert.DeserializeObject(exportCapDto.Parameters.ToString(), x.ParameterType))
        //        .ToArray();

        //    object o = method.Invoke(obj, parameters);
        //    if (o is Task)
        //    {
        //        var task = o as Task;
        //        await task;
        //        o = task.GetType().GetProperty("Result").GetValue(task, null);
        //    }

        //    dynamic result = o;

        //    var data = result.Data;

        //    //todo 有问题 --------------------------------------

        //    //Type datatype = data.GetType();
        //    //if (datatype.IsGenericType)
        //    //{
        //    //    List<PropertyInfo> tProperties22 = datatype.GetGenericArguments().First().GetProperties().ToList();
        //    //}
        //    //else
        //    //{
        //    //    throw new Exception();
        //    //}
        //    var excelTitleList = new List<ExcelTitleDto>();

        //    List<PropertyInfo> tProperties = data.GetType().GetGenericArguments().First().GetProperties().ToList();
        //    //List<PropertyInfo> tProperties = data.First().GetType().GetProperties().ToList();

        //    tProperties.ForEach(item =>
        //    {
        //        var itemCustomAttributes = item.GetCustomAttributes(true);
        //        var itemDtoExportAttribute = itemCustomAttributes.Where(x => x is DtoExportAttribute).FirstOrDefault();
        //        if (itemDtoExportAttribute != null)
        //        {
        //            excelTitleList.Add(new ExcelTitleDto
        //            {
        //                PropertityName = item.Name,
        //                TitleName = (itemDtoExportAttribute as DtoExportAttribute).TitleName,
        //                Width = (itemDtoExportAttribute as DtoExportAttribute).Width,
        //                PropertyInfo = item
        //            });
        //        }
        //    });

        //    var excelDto = new ExcelDto<dynamic>
        //    {
        //        Data = data,
        //        ExcelTitleDtos = excelTitleList,
        //        Guid = taskPerformance.Guid,
        //        TaskId = taskPerformance.Id
        //    };
        //    await FillExcel(excelDto);
        //}
        //private async Task FillExcel(ExcelDto<dynamic> excelDto)
        //{
        //    List<ExcelTitleDto> excelTitleDtos = excelDto.ExcelTitleDtos;
        //    var data = excelDto.Data;
        //    string guid = excelDto.Guid;


        //    var workbook = new XSSFWorkbook();
        //    var sheet = workbook.CreateSheet();

        //    //设置标题
        //    var row = sheet.CreateRow(0);
        //    #region 标题居中
        //    var headFont = workbook.CreateFont();
        //    headFont.IsBold = true;
        //    var headStyle = workbook.CreateCellStyle();
        //    headStyle.Alignment = HorizontalAlignment.Center;
        //    headStyle.SetFont(headFont);
        //    #endregion
        //    for (int i = 0; i < excelTitleDtos.Count; i++)
        //    {
        //        var item = excelTitleDtos[i];
        //        sheet.SetColumnWidth(i, item.Width * 2 * 256);

        //        var cell = row.CreateCell(i);
        //        cell.CellStyle = headStyle;
        //        cell.SetCellValue(item.TitleName);
        //    }

        //    //设置内容 从第一行开始
        //    for (int i = 0; i < data.Count; i++)
        //    {
        //        var item = data[i];
        //        row = sheet.CreateRow(i + 1);
        //        for (int j = 0; j < excelTitleDtos.Count; j++)
        //        {
        //            string cellValue;//单元格内容

        //            var itemPropertyInfo = excelTitleDtos[j].PropertyInfo;
        //            var itemValue = itemPropertyInfo.GetValue(item, null);
        //            if (itemValue == null)
        //            {
        //                cellValue = "";
        //            }
        //            else if (itemPropertyInfo.PropertyType == typeof(DateTime) || itemPropertyInfo.PropertyType == typeof(DateTime?))
        //            {
        //                cellValue = ((DateTime)itemValue).ToString("yyyy-MM-dd HH:mm:ss");
        //            }
        //            else
        //            {
        //                cellValue = itemValue.ToString();
        //            }

        //            row.CreateCell(j).SetCellValue(cellValue);
        //        }
        //    }

        //    //if (isIsImmediately)
        //    //{
        //    //    using MemoryStream file = new();
        //    //    workbook.Write(file);
        //    //    file.Close();
        //    //    return await Task.FromResult(file);
        //    //}

        //    var task = await _taskPerformanceRepository.GetByIdAsync(excelDto.TaskId);
        //    try
        //    {
        //        //Excel的路径及名称
        //        string excelPath = Path.Combine(_folderPath, $"{guid}.xlsx");
        //        using FileStream fs = new(excelPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
        //        workbook.Write(fs);
        //        fs.Close();
        //        task.TaskExeStatus = TaskExeStatus.Success;
        //    }
        //    catch (Exception)
        //    {
        //        //可以记录日志
        //        task.TaskExeStatus = TaskExeStatus.Fail;
        //    }
        //    await _taskPerformanceRepository.UpdateAsync(task);
        //}
        #endregion

        /// <summary>
        /// 查询任务执行情况
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PageData<TaskPerformanceDto>> GetTaskPerformancePageData(QueryTaskPerformanceDto input)
        {
            var query = (await _taskPerformanceRepository.GetAllAsync()).Where(x => input.Code == x.Code || string.IsNullOrEmpty(input.Code));
            var result = query.ToPageList(input.Pages, input.Rows);
            return _mapper.Map<PageData<TaskPerformanceDto>>(result);
        }

        /// <summary>
        /// 获取导出任务
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<TaskPerformanceDto> GetTaskPerformance(int id)
        {
            return _mapper.Map<TaskPerformanceDto>(await _taskPerformanceRepository.GetByIdAsync(id));
        }

        /// <summary>
        /// 添加任务
        /// </summary>
        /// <param name="taskPerformanceDto"></param>
        /// <returns></returns>
        public async Task<TaskPerformanceDto> AddTaskPerformance(TaskPerformanceDto taskPerformanceDto)
        {
            var entity = _mapper.Map<TaskPerformance>(taskPerformanceDto);
            await _taskPerformanceRepository.AddAsync(entity);
            return _mapper.Map<TaskPerformanceDto>(entity);
        }
        #region 导出

        public async Task<TaskPerformance> GetExportTask(ExportDto exportDto)
        {
            var taskPerformance = new TaskPerformance
            {
                TaskExeStatus = TaskExeStatus.No,
                Code = exportDto.Code,
                FileName = $"{exportDto.FileName}",
                FileType = "xlsx",
                Guid = Guid.NewGuid().ToString("N")
            };
            await _taskPerformanceRepository.AddAsync(taskPerformance);
            return taskPerformance;
        }

        public async Task Export<TQuery, TRes>(TQuery input, Func<TQuery, Task<PageData<TRes>>> func, int taskPerformanceId)
        {
            var taskPerformance = await _taskPerformanceRepository.GetByIdAsync(taskPerformanceId);
            var res = await func(input);
            var data = res.Data;
            var excelTitleList = new List<ExcelTitleDto>();
            List<PropertyInfo> tProperties = typeof(TRes).GetProperties().ToList();

            tProperties.ForEach(item =>
            {
                var itemCustomAttributes = item.GetCustomAttributes(true);
                var itemDtoExportAttribute = itemCustomAttributes.Where(x => x is DtoExportAttribute).FirstOrDefault();
                if (itemDtoExportAttribute != null)
                {
                    excelTitleList.Add(new ExcelTitleDto
                    {
                        PropertityName = item.Name,
                        TitleName = (itemDtoExportAttribute as DtoExportAttribute).TitleName,
                        Width = (itemDtoExportAttribute as DtoExportAttribute).Width,
                        PropertyInfo = item
                    });
                }
            });

            var excelDto = new ExcelDto<TRes>
            {
                Data = data,
                ExcelTitleDtos = excelTitleList,
                Guid = taskPerformance.Guid,
                TaskId = taskPerformance.Id
            };
            await FillExcel(excelDto);
        }
        private async Task FillExcel<TRes>(ExcelDto<TRes> excelDto)
        {
            List<ExcelTitleDto> excelTitleDtos = excelDto.ExcelTitleDtos;
            var data = excelDto.Data;
            string guid = excelDto.Guid;


            var workbook = new XSSFWorkbook();
            var sheet = workbook.CreateSheet();

            //设置标题
            var row = sheet.CreateRow(0);
            #region 标题居中
            var headFont = workbook.CreateFont();
            headFont.IsBold = true;
            var headStyle = workbook.CreateCellStyle();
            headStyle.Alignment = HorizontalAlignment.Center;
            headStyle.SetFont(headFont);
            #endregion
            for (int i = 0; i < excelTitleDtos.Count; i++)
            {
                var item = excelTitleDtos[i];
                sheet.SetColumnWidth(i, item.Width * 2 * 256);

                var cell = row.CreateCell(i);
                cell.CellStyle = headStyle;
                cell.SetCellValue(item.TitleName);
            }

            //设置内容 从第一行开始
            for (int i = 0; i < data.Count; i++)
            {
                var item = data[i];
                row = sheet.CreateRow(i + 1);
                for (int j = 0; j < excelTitleDtos.Count; j++)
                {
                    string cellValue;//单元格内容

                    var itemPropertyInfo = excelTitleDtos[j].PropertyInfo;
                    var itemValue = itemPropertyInfo.GetValue(item, null);
                    if (itemValue == null)
                    {
                        cellValue = "";
                    }
                    else if (itemPropertyInfo.PropertyType == typeof(DateTime) || itemPropertyInfo.PropertyType == typeof(DateTime?))
                    {
                        cellValue = ((DateTime)itemValue).ToString("yyyy-MM-dd HH:mm:ss");
                    }
                    else
                    {
                        cellValue = itemValue.ToString();
                    }

                    row.CreateCell(j).SetCellValue(cellValue);
                }
            }

            //if (isIsImmediately)
            //{
            //    using MemoryStream file = new();
            //    workbook.Write(file);
            //    file.Close();
            //    return await Task.FromResult(file);
            //}

            var task = await _taskPerformanceRepository.GetByIdAsync(excelDto.TaskId);
            try
            {
                //Excel的路径及名称
                string excelPath = Path.Combine(_folderPath, $"{guid}.xlsx");
                using FileStream fs = new(excelPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                workbook.Write(fs);
                fs.Close();
                task.TaskExeStatus = TaskExeStatus.Success;
            }
            catch (Exception)
            {
                //可以记录日志
                task.TaskExeStatus = TaskExeStatus.Fail;
            }
            await _taskPerformanceRepository.UpdateAsync(task);
        }
        #endregion

        #region 导入
        /// <summary>
        /// excel转换data
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="taskPerformanceId"></param>
        /// <returns></returns>
        public async Task<List<T>> ConvertExcelToData<T>(int taskPerformanceId)
        {
            var wk = await GetExcelData(taskPerformanceId);
            List<T> result = new List<T>();
            List<ExcelTitleDto> excelTitleDtos = new List<ExcelTitleDto>();

            List<PropertyInfo> tPropertyInfo = typeof(T).GetProperties().ToList();

            tPropertyInfo.ForEach(item =>
            {
                var itemCustomAttributes = item.GetCustomAttributes(true);
                var itemDtoExportAttribute = itemCustomAttributes.Where(x => x is DtoExportAttribute).FirstOrDefault();
                if (itemDtoExportAttribute != null)
                {
                    excelTitleDtos.Add(new ExcelTitleDto
                    {
                        TitleName = (itemDtoExportAttribute as DtoExportAttribute).TitleName,
                        PropertityName = item.Name
                    });
                }
            });



            ISheet sheet = wk.GetSheetAt(0);//读取当前表数据
            IRow row = sheet.GetRow(0);  //读取当前行数据

            Dictionary<int, string> relationDic = new Dictionary<int, string>();//记录关系

            //LastRowNum 是当前表的总行数-1（注意）
            for (int i = 0; i <= sheet.LastRowNum; i++)
            {
                T tObject = Activator.CreateInstance<T>();
                // Console.Write("序号："+i);
                row = sheet.GetRow(i);  //读取当前行数据

                if (row == null)
                {
                    break;
                }

                if (i == 0)
                {
                    for (int j = 0; j < row.LastCellNum; j++)
                    {
                        string value = row.GetCell(j).ToString();
                        var propertyName = excelTitleDtos.Where(x => x.TitleName == value).FirstOrDefault().PropertityName;
                        relationDic.Add(j, propertyName);
                    }
                }
                else
                {
                    for (int j = 0; j < row.LastCellNum; j++)
                    {
                        var currentProperty = tPropertyInfo.Where(x => x.Name == relationDic[j]).FirstOrDefault();
                        string value = row.GetCell(j).ToString();
                        currentProperty.SetValue(tObject, value.FormatString(currentProperty.PropertyType));
                    }
                    result.Add(tObject);
                }
            }
            return result;
        }
        private async Task<IWorkbook> GetExcelData(int taskPerformanceId)
        {
            var task = await _taskPerformanceRepository.GetByIdAsync(taskPerformanceId);
            // 导入总表
            IWorkbook wk = null;
            string path = Path.Combine(_webHostEnvironment.ContentRootPath, "File", $"{task.Guid}.{task.FileType}");
            string extension = System.IO.Path.GetExtension(path);
            FileStream fs = System.IO.File.OpenRead(path);
            if (extension.Equals(".xls"))
            {
                //把xls文件中的数据写入wk中
                wk = new HSSFWorkbook(fs);
            }
            else
            {
                //把xlsx文件中的数据写入wk中
                wk = new XSSFWorkbook(fs);
            }

            fs.Close();

            return wk;
        }
        #endregion
    }
}
